{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Document Query Example\n",
    "\n",
    "This notebook demonstrates how to use the LLM Python API to ask questions from a document (a pdf).\n",
    "\n",
    "Let's install all the dependencies first. Make sure to use `--upgrade` to get the latest version since most of the packages are under active development."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: langchain in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (0.0.147)\n",
      "Collecting langchain\n",
      "  Downloading langchain-0.0.148-py3-none-any.whl (636 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m636.7/636.7 kB\u001b[0m \u001b[31m35.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hRequirement already satisfied: tenacity<9.0.0,>=8.1.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from langchain) (8.2.2)\n",
      "Requirement already satisfied: numexpr<3.0.0,>=2.8.4 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from langchain) (2.8.4)\n",
      "Requirement already satisfied: async-timeout<5.0.0,>=4.0.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from langchain) (4.0.2)\n",
      "Requirement already satisfied: requests<3,>=2 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from langchain) (2.28.2)\n",
      "Requirement already satisfied: numpy<2,>=1 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from langchain) (1.24.3)\n",
      "Requirement already satisfied: aiohttp<4.0.0,>=3.8.3 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from langchain) (3.8.4)\n",
      "Requirement already satisfied: tqdm>=4.48.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from langchain) (4.65.0)\n",
      "Requirement already satisfied: PyYAML>=5.4.1 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from langchain) (6.0)\n",
      "Requirement already satisfied: SQLAlchemy<2,>=1 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from langchain) (1.4.47)\n",
      "Requirement already satisfied: dataclasses-json<0.6.0,>=0.5.7 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from langchain) (0.5.7)\n",
      "Requirement already satisfied: openapi-schema-pydantic<2.0,>=1.2 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from langchain) (1.2.4)\n",
      "Requirement already satisfied: pydantic<2,>=1 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from langchain) (1.10.6)\n",
      "Requirement already satisfied: attrs>=17.3.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (22.2.0)\n",
      "Requirement already satisfied: multidict<7.0,>=4.5 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (6.0.4)\n",
      "Requirement already satisfied: yarl<2.0,>=1.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.8.2)\n",
      "Requirement already satisfied: aiosignal>=1.1.2 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.3.1)\n",
      "Requirement already satisfied: frozenlist>=1.1.1 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.3.3)\n",
      "Requirement already satisfied: charset-normalizer<4.0,>=2.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (3.1.0)\n",
      "Requirement already satisfied: marshmallow-enum<2.0.0,>=1.5.1 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from dataclasses-json<0.6.0,>=0.5.7->langchain) (1.5.1)\n",
      "Requirement already satisfied: marshmallow<4.0.0,>=3.3.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from dataclasses-json<0.6.0,>=0.5.7->langchain) (3.19.0)\n",
      "Requirement already satisfied: typing-inspect>=0.4.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from dataclasses-json<0.6.0,>=0.5.7->langchain) (0.8.0)\n",
      "Requirement already satisfied: typing-extensions>=4.2.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from pydantic<2,>=1->langchain) (4.5.0)\n",
      "Requirement already satisfied: idna<4,>=2.5 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from requests<3,>=2->langchain) (3.4)\n",
      "Requirement already satisfied: urllib3<1.27,>=1.21.1 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from requests<3,>=2->langchain) (1.26.15)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from requests<3,>=2->langchain) (2022.12.7)\n",
      "Requirement already satisfied: greenlet!=0.4.17 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from SQLAlchemy<2,>=1->langchain) (2.0.2)\n",
      "Requirement already satisfied: packaging>=17.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from marshmallow<4.0.0,>=3.3.0->dataclasses-json<0.6.0,>=0.5.7->langchain) (23.0)\n",
      "Requirement already satisfied: mypy-extensions>=0.3.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from typing-inspect>=0.4.0->dataclasses-json<0.6.0,>=0.5.7->langchain) (1.0.0)\n",
      "Installing collected packages: langchain\n",
      "  Attempting uninstall: langchain\n",
      "    Found existing installation: langchain 0.0.147\n",
      "    Uninstalling langchain-0.0.147:\n",
      "      Successfully uninstalled langchain-0.0.147\n",
      "Successfully installed langchain-0.0.148\n",
      "Requirement already satisfied: openai in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (0.27.2)\n",
      "Collecting openai\n",
      "  Using cached openai-0.27.4-py3-none-any.whl (70 kB)\n",
      "Requirement already satisfied: aiohttp in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from openai) (3.8.4)\n",
      "Requirement already satisfied: tqdm in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from openai) (4.65.0)\n",
      "Requirement already satisfied: requests>=2.20 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from openai) (2.28.2)\n",
      "Requirement already satisfied: urllib3<1.27,>=1.21.1 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from requests>=2.20->openai) (1.26.15)\n",
      "Requirement already satisfied: idna<4,>=2.5 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from requests>=2.20->openai) (3.4)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from requests>=2.20->openai) (2022.12.7)\n",
      "Requirement already satisfied: charset-normalizer<4,>=2 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from requests>=2.20->openai) (3.1.0)\n",
      "Requirement already satisfied: frozenlist>=1.1.1 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from aiohttp->openai) (1.3.3)\n",
      "Requirement already satisfied: async-timeout<5.0,>=4.0.0a3 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from aiohttp->openai) (4.0.2)\n",
      "Requirement already satisfied: aiosignal>=1.1.2 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from aiohttp->openai) (1.3.1)\n",
      "Requirement already satisfied: attrs>=17.3.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from aiohttp->openai) (22.2.0)\n",
      "Requirement already satisfied: yarl<2.0,>=1.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from aiohttp->openai) (1.8.2)\n",
      "Requirement already satisfied: multidict<7.0,>=4.5 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from aiohttp->openai) (6.0.4)\n",
      "Installing collected packages: openai\n",
      "  Attempting uninstall: openai\n",
      "    Found existing installation: openai 0.27.2\n",
      "    Uninstalling openai-0.27.2:\n",
      "      Successfully uninstalled openai-0.27.2\n",
      "Successfully installed openai-0.27.4\n",
      "Requirement already satisfied: unstructured in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (0.6.1)\n",
      "Requirement already satisfied: openpyxl in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from unstructured) (3.1.2)\n",
      "Requirement already satisfied: nltk in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from unstructured) (3.8.1)\n",
      "Requirement already satisfied: pandas in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from unstructured) (1.5.3)\n",
      "Requirement already satisfied: argilla in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from unstructured) (1.5.1)\n",
      "Requirement already satisfied: msg-parser in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from unstructured) (1.2.0)\n",
      "Requirement already satisfied: python-docx in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from unstructured) (0.8.11)\n",
      "Requirement already satisfied: python-pptx in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from unstructured) (0.6.21)\n",
      "Requirement already satisfied: pillow in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from unstructured) (9.5.0)\n",
      "Requirement already satisfied: python-magic in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from unstructured) (0.4.27)\n",
      "Requirement already satisfied: certifi>=2022.12.07 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from unstructured) (2022.12.7)\n",
      "Requirement already satisfied: markdown in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from unstructured) (3.3.7)\n",
      "Requirement already satisfied: pypandoc in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from unstructured) (1.11)\n",
      "Requirement already satisfied: requests in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from unstructured) (2.28.2)\n",
      "Requirement already satisfied: lxml in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from unstructured) (4.9.2)\n",
      "Collecting numpy<1.24.0\n",
      "  Using cached numpy-1.23.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (17.1 MB)\n",
      "Requirement already satisfied: pydantic>=1.7.1 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from argilla->unstructured) (1.10.6)\n",
      "Requirement already satisfied: tqdm>=4.27.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from argilla->unstructured) (4.65.0)\n",
      "Requirement already satisfied: packaging>=20.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from argilla->unstructured) (23.0)\n",
      "Requirement already satisfied: rich<=13.0.1 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from argilla->unstructured) (13.0.1)\n",
      "Requirement already satisfied: wrapt<1.15,>=1.13 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from argilla->unstructured) (1.14.1)\n",
      "Requirement already satisfied: deprecated~=1.2.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from argilla->unstructured) (1.2.13)\n",
      "Requirement already satisfied: monotonic in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from argilla->unstructured) (1.6)\n",
      "Requirement already satisfied: backoff in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from argilla->unstructured) (2.2.1)\n",
      "Requirement already satisfied: httpx<0.24,>=0.15 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from argilla->unstructured) (0.23.3)\n",
      "Requirement already satisfied: python-dateutil>=2.8.1 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from pandas->unstructured) (2.8.2)\n",
      "Requirement already satisfied: pytz>=2020.1 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from pandas->unstructured) (2022.7.1)\n",
      "Requirement already satisfied: olefile>=0.46 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from msg-parser->unstructured) (0.46)\n",
      "Requirement already satisfied: regex>=2021.8.3 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from nltk->unstructured) (2022.10.31)\n",
      "Requirement already satisfied: joblib in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from nltk->unstructured) (1.2.0)\n",
      "Requirement already satisfied: click in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from nltk->unstructured) (8.1.3)\n",
      "Requirement already satisfied: et-xmlfile in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from openpyxl->unstructured) (1.1.0)\n",
      "Requirement already satisfied: XlsxWriter>=0.5.7 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from python-pptx->unstructured) (3.0.9)\n",
      "Requirement already satisfied: charset-normalizer<4,>=2 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from requests->unstructured) (3.1.0)\n",
      "Requirement already satisfied: urllib3<1.27,>=1.21.1 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from requests->unstructured) (1.26.15)\n",
      "Requirement already satisfied: idna<4,>=2.5 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from requests->unstructured) (3.4)\n",
      "Requirement already satisfied: httpcore<0.17.0,>=0.15.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from httpx<0.24,>=0.15->argilla->unstructured) (0.16.3)\n",
      "Requirement already satisfied: rfc3986[idna2008]<2,>=1.3 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from httpx<0.24,>=0.15->argilla->unstructured) (1.5.0)\n",
      "Requirement already satisfied: sniffio in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from httpx<0.24,>=0.15->argilla->unstructured) (1.3.0)\n",
      "Requirement already satisfied: typing-extensions>=4.2.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from pydantic>=1.7.1->argilla->unstructured) (4.5.0)\n",
      "Requirement already satisfied: six>=1.5 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from python-dateutil>=2.8.1->pandas->unstructured) (1.16.0)\n",
      "Requirement already satisfied: commonmark<0.10.0,>=0.9.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from rich<=13.0.1->argilla->unstructured) (0.9.1)\n",
      "Requirement already satisfied: pygments<3.0.0,>=2.6.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from rich<=13.0.1->argilla->unstructured) (2.14.0)\n",
      "Requirement already satisfied: h11<0.15,>=0.13 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from httpcore<0.17.0,>=0.15.0->httpx<0.24,>=0.15->argilla->unstructured) (0.14.0)\n",
      "Requirement already satisfied: anyio<5.0,>=3.0 in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (from httpcore<0.17.0,>=0.15.0->httpx<0.24,>=0.15->argilla->unstructured) (3.6.2)\n",
      "Installing collected packages: numpy\n",
      "  Attempting uninstall: numpy\n",
      "    Found existing installation: numpy 1.24.3\n",
      "    Uninstalling numpy-1.24.3:\n",
      "      Successfully uninstalled numpy-1.24.3\n",
      "Successfully installed numpy-1.23.5\n",
      "Requirement already satisfied: pypdf in /home/rowel/anaconda3/envs/llm/lib/python3.10/site-packages (3.8.1)\n"
     ]
    }
   ],
   "source": [
    "! pip install langchain --upgrade\n",
    "! pip install openai --upgrade\n",
    "! pip install unstructured --upgrade\n",
    "! pip install pypdf --upgrade"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Import packages\n",
    "\n",
    "We import the following packages. Note that we did not import OpenAI since it already used by LangChain under the hood."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.document_loaders import PyPDFLoader\n",
    "from langchain.indexes import VectorstoreIndexCreator\n",
    "import os\n",
    "import textwrap"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Upload a PDF of your choice\n",
    "\n",
    "Upload a PDF of your choice by providing its link or url. In this case, the pdf is a student manual.\n",
    "\n",
    "From the given link, a document loader is created. Some basic information (eg number of pages) about the document is printed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "UP_Academic_Information.pdf number of pages = 69\n"
     ]
    }
   ],
   "source": [
    "pdf_url = input(\"Enter pdf url: \")\n",
    "\n",
    "# eg https://ac.upd.edu.ph/acmedia/images/newpdfs/UP_Academic_Information.pdf\n",
    "loader = PyPDFLoader(pdf_url)\n",
    "pages = loader.load_and_split()\n",
    "\n",
    "# for the url, get the document name\n",
    "document_name = pdf_url.split(\"/\")[-1]\n",
    "document_len = len(pages)\n",
    "print(f\"{document_name} number of pages = {document_len}\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Enter your OpenAI API key\n",
    "\n",
    "To use OpenAI LLM, enter your API key. You can get one from [here](https://beta.openai.com/).\n",
    "\n",
    "The key will be used to create a vector store or database of embeddings for the document. OpenAI converts the document tokens (like words or part of words) into vectors (embeddings). This vectorstore is used to find the most similar tokens to a given query. The tokens are then passed to the LLM model to generate the answer.\n",
    "\n",
    "This step may take a few minutes. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using embedded DuckDB without persistence: data will be transient\n"
     ]
    }
   ],
   "source": [
    "query = input(\"OpenAI API Key: \")\n",
    "os.environ[\"OPENAI_API_KEY\"] = query\n",
    "index = VectorstoreIndexCreator().from_loaders([loader])"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Ask a question\n",
    "\n",
    "Now that we have a vector store for the document, we can ask questions about it.  The answer from OpenAI LLM is printed.\n",
    "\n",
    "The session will stop once the human user says \"bye\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "question: how many weeks per semester\n",
      "answer:  Each semester consists of at least sixteen (16) weeks.\n",
      "sources: /tmp/tmpptj7z6sa\n",
      "question: who approves the candidacy for graduation\n",
      "answer:  The Dean or the duly authorized representative submits a list of\n",
      "candidates for graduation to the University Registrar, who then inquires into\n",
      "the academic records of the candidates to ascertain whether they have satisfied\n",
      "all requirements for graduation.\n",
      "sources: /tmp/tmpptj7z6sa\n",
      "question: what is crs\n",
      "answer:  CRS stands for the Maximum Residence Rule.\n",
      "sources: /tmp/tmpptj7z6sa\n",
      "question: tell me more about crs\n",
      "answer:  The Computerized Registration System (CRS) allows UP Diliman enrollees\n",
      "to register for particular subjects within a semester when permissible under the\n",
      "system of instruction adopted by the college.\n",
      "sources: /tmp/tmpptj7z6sa\n",
      "question: who can use crs\n",
      "answer:  The Computerized Registration System (CRS) allows UP Diliman enrollees\n",
      "to register for particular subjects within a semester when permissible under the\n",
      "system of instruction adopted by the college.\n",
      "sources: /tmp/tmpptj7z6sa\n",
      "Human: : bye\n",
      "AI: Bye!\n"
     ]
    }
   ],
   "source": [
    "while True:\n",
    "    input_prompt = \"Human: \"\n",
    "    query = input(input_prompt)\n",
    "\n",
    "    if query.lower() == \"bye\":\n",
    "        text = f\"{input_prompt}: {query}\"\n",
    "        print(textwrap.fill(text, width=80))\n",
    "        print(\"AI: Bye!\")\n",
    "        break\n",
    "\n",
    "    # print text within page width\n",
    "    for key,value in index.query_with_sources(query).items():\n",
    "        text = f\"{key}: {value}\"\n",
    "        print(textwrap.fill(text, width=80))\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "llm",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.9"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
